using Microsoft.Extensions.Primitives;
using Org.BouncyCastle.Utilities;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;

namespace QQZiFramework
{
    public class BaseQueryParamConditionBuilder
    {
        //当前条件组的指针，Or endOr 的时候出入栈
        protected Stack<QueryParamConditionGroup> groupStackPoint = new Stack<QueryParamConditionGroup>();

        protected QueryParamConditionGroup conditionGroup;

        protected DBContext context;

        protected QueryParam queryParam;

        public BaseQueryParamConditionBuilder()
        {
            conditionGroup = new QueryParamConditionGroup()
            {
                Conjunction = QueryParamConditionGroup.ConditionGroupConjunction.and

            };
            groupStackPoint.Push(conditionGroup);
        }

        internal void SetContext(DBContext context, QueryParam param)
        {
            this.context = context;
            this.queryParam = param;
        }

        internal QueryParamConditionGroup GetDefaultGroup()
        {
            return conditionGroup;
        }

        Dictionary<string, int> fieldCountInWhereCondition = new Dictionary<string, int>();//where字段次数，比如有两个Id的条件，需要生成参数的时候重名
        internal void GenGroupWhereSql(QueryEntityInstance queryInstance, QueryParamConditionGroup group, List<IDbDataParameter> parameterList, StringBuilder sb)
        {

            //如果有group by 放在最后
            string groupby = "";
            Type entityType = queryInstance.Param.entityType;


            var conjunction = (group.Conjunction == QueryParamConditionGroup.ConditionGroupConjunction.and ? "and" : "or");
            bool isFirst = true;
            foreach (QueryParamConditionItem ki in group.Items)
            {
                IDbDataParameter[] itemParameters;
                var strItem = queryInstance.CreateWhereItemParameters(ki.FieldName, ki.WhereItem, this.context, out itemParameters);
                if (itemParameters != null)
                {
                    //判断参数名是否重复                   
                    if (fieldCountInWhereCondition.ContainsKey(ki.FieldName))
                    {
                        int times = ++fieldCountInWhereCondition[ki.FieldName];
                        //重名掉
                        foreach (var item in itemParameters)
                        {
                            string oldName = item.ParameterName;
                            item.ParameterName = item.ParameterName + "_" + times;
                            strItem = strItem.Replace(oldName, item.ParameterName);
                        }
                    }
                    else
                    {
                        fieldCountInWhereCondition.Add(ki.FieldName, 1);

                    }
                    parameterList.AddRange(itemParameters);
                }
                if (ki.WhereItem is WhereItem_GroupBy || ((ki.WhereItem is WhereItem_Custom) && ((WhereItem_Custom)ki.WhereItem).IsGroupBy))
                {
                    groupby = strItem;
                }
                else
                {
                    if (!isFirst)
                    {
                        sb.Append(" ");
                        sb.Append( conjunction);
                        sb.Append(" ");
                    }
                    sb.Append("(");
                    if (ki.WhereItem.ConcatTemplate != null)
                    {
                        sb.Append(ki.WhereItem.ConcatTemplate.Replace("$$", strItem, StringComparison.OrdinalIgnoreCase));
                    }
                    else
                    {
                        sb.Append(strItem);
                    }
                    sb.Append(")");
                    isFirst = false;
                }
            }


            foreach (QueryParamConditionGroup childGroup in group.Groups)
            {
                if (sb.Length > 0)
                {
                    sb.Append(" ");
                    sb.Append(conjunction);
                    sb.Append(" ");
                }
                sb.Append("(");
                GenGroupWhereSql(queryInstance, childGroup, parameterList, sb);
                sb.Append(")");
            }

            // if (sb.Length > 0) WhereSql = sb.ToString().Substring(5);
            if (groupby.Length > 1)
            {
                if (sb.Length > 1)
                {
                    sb.Append(" " + groupby.Replace("1=1", ""));
                }
                else
                {
                    //没有其它条件，后面会拼where所以要加1=1
                    sb.Append(" 1=1 " + groupby.Replace("1=1", ""));
                }
            }

        }

        internal string GenGroupWhereSql(QueryEntityInstance queryInstance, out IDbDataParameter[] parameters)
        {
            fieldCountInWhereCondition.Clear();
            StringBuilder sb = new StringBuilder();
            List<IDbDataParameter> parameterList = new List<IDbDataParameter>();
            GenGroupWhereSql(queryInstance, GetDefaultGroup(), parameterList, sb);
            parameters = parameterList.ToArray();
            fieldCountInWhereCondition.Clear();
            return sb.ToString();
        }



    }

    public class QueryParamConditionBuilder<T> : BaseQueryParamConditionBuilder where T : IDBEntity
    {

        /// <summary>
        /// 添加or条件组，需要以endOr结束
        /// </summary>
        public QueryParamConditionBuilder<T> Or()
        {
            var current = groupStackPoint.Peek();
            QueryParamConditionGroup newOrGroup = new QueryParamConditionGroup() { Conjunction = QueryParamConditionGroup.ConditionGroupConjunction.or };
            groupStackPoint.Push(newOrGroup);
            current.Groups.Add(newOrGroup);
            return this;
        }

        /// <summary>
        /// 结束添加or条件组
        /// </summary>
        public QueryParamConditionBuilder<T> endOr()
        {

            var current = groupStackPoint.Peek();
            if (current.Conjunction != QueryParamConditionGroup.ConditionGroupConjunction.or)
            {
                throw new System.Exception("endOr需要先调用.Or方法");
            }
            groupStackPoint.Pop();
            return this;
        }

        /// <summary>
        /// 添加and条件组，需要以EndAnd结束
        /// </summary>
        public QueryParamConditionBuilder<T> And()
        {
            var current = groupStackPoint.Peek();
            QueryParamConditionGroup newAndGroup = new QueryParamConditionGroup() { Conjunction = QueryParamConditionGroup.ConditionGroupConjunction.and };
            groupStackPoint.Push(newAndGroup);
            current.Groups.Add(newAndGroup);
            return this;

        }
        /// <summary>
        /// 结束添加or条件组
        /// </summary>
        public QueryParamConditionBuilder<T> EndAnd()
        {

            var current = groupStackPoint.Peek();
            if (current.Conjunction != QueryParamConditionGroup.ConditionGroupConjunction.and)
            {
                throw new System.Exception("endOr需要先调用.Or方法");
            }
            groupStackPoint.Pop();
            return this;
        }

        /// <summary>
        /// 添加条件，在初始化Param但未开始查询的时候可调用
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="exp">自定义表达式，需要带${field}或字段名</param>
        /// <param name="val">值，会被使用sql参数替换</param>
        public QueryParamConditionBuilder<T> Custom(string fieldName, string exp, params object[] val)
        {
            var whereItem = new WhereItem_Custom(exp, val);
            var current = groupStackPoint.Peek();
            current.Items.Add(new QueryParamConditionItem(fieldName, whereItem));
            return this;
        }

        /// <summary>
        /// 添加=条件，在初始化Param但未开始查询的时候可调用
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="value">值，多个使用|隔开，对应permission.Fields {0}|{1}...</param>
        public QueryParamConditionBuilder<T> Equal(string fieldName, object value)
        {
            return Eq(fieldName,value);
        }

        public QueryParamConditionBuilder<T> Eq(string fieldName, object value)
        {
            var whereItem = new WhereItem_Eq(value);
            var current = groupStackPoint.Peek();
            current.Items.Add(new QueryParamConditionItem(fieldName, whereItem));
            return this;
        }

        /// <summary>
        /// 添加=条件，在初始化Param但未开始查询的时候可调用
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="value">值，多个使用|隔开，对应permission.Fields {0}|{1}...</param>
        public QueryParamConditionBuilder<T> NotEqual(string fieldName, object value)
        {
            return NEq(fieldName,value);
        }
        /// <summary>
        /// 添加=条件，在初始化Param但未开始查询的时候可调用
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="value">值，多个使用|隔开，对应permission.Fields {0}|{1}...</param>
        public QueryParamConditionBuilder<T> NEq(string fieldName, object value)
        {
            var whereItem = new WhereItem_NotEq(value);
            var current = groupStackPoint.Peek();
            current.Items.Add(new QueryParamConditionItem(fieldName, whereItem));
            return this;
        }

        /// <summary>
        /// 添加in条件，在初始化Param但未开始查询的时候可调用
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="value">值，多个使用|隔开，对应permission.Fields {0}|{1}...</param>
        public QueryParamConditionBuilder<T> In(string fieldName, object[] values)
        {
            var whereItem = new WhereItem_In(values);
            var current = groupStackPoint.Peek();
            current.Items.Add(new QueryParamConditionItem(fieldName, whereItem));
            return this;
        }


        /// <summary>
        /// 添加Between条件，在初始化Param但未开始查询的时候可调用
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="value">值，多个使用|隔开，对应permission.Fields {0}|{1}...</param>
        public QueryParamConditionBuilder<T> Between(string fieldName, object start, object end)
        {
            var whereItem = new WhereItem_Between(start, end);
            var current = groupStackPoint.Peek();
            current.Items.Add(new QueryParamConditionItem(fieldName, whereItem));
            return this;
        }

        /// <summary>
        /// 添加大于条件，在初始化Param但未开始查询的时候可调用
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="value">值，多个使用|隔开，对应permission.Fields {0}|{1}...</param>
        public QueryParamConditionBuilder<T> Gt(string fieldName, object value)
        {
            var whereItem = new WhereItem_Custom("${field} > {0}", value);
            var current = groupStackPoint.Peek();
            current.Items.Add(new QueryParamConditionItem(fieldName, whereItem));
            return this;
        }

        /// <summary>
        /// 添加大于条件，在初始化Param但未开始查询的时候可调用
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="value">值，多个使用|隔开，对应permission.Fields {0}|{1}...</param>
        public QueryParamConditionBuilder<T> Lt(string fieldName, object value)
        {
            var whereItem = new WhereItem_Custom("${field} < {0}", value);
            var current = groupStackPoint.Peek();
            current.Items.Add(new QueryParamConditionItem(fieldName, whereItem));
            return this;
        }

        /// <summary>
        /// 添加isnull条件，在初始化Param但未开始查询的时候可调用
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="value">值，多个使用|隔开，对应permission.Fields {0}|{1}...</param>
        public QueryParamConditionBuilder<T> IsNull(string fieldName)
        {
            var whereItem = new WhereItem_Custom("${field} is null", null);
            var current = groupStackPoint.Peek();
            current.Items.Add(new QueryParamConditionItem(fieldName, whereItem));
            return this;
        }

        /// <summary>
        /// 添加is not null条件，在初始化Param但未开始查询的时候可调用
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="value">值，多个使用|隔开，对应permission.Fields {0}|{1}...</param>
        public QueryParamConditionBuilder<T> NotNull(string fieldName)
        {
            var whereItem = new WhereItem_Custom("${field} is not null", null);
            var current = groupStackPoint.Peek();
            current.Items.Add(new QueryParamConditionItem(fieldName, whereItem));
            return this;
        }



        /// <summary>
        /// 添加like条件，在初始化Param但未开始查询的时候可调用
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="value">值，多个使用|隔开，对应permission.Fields {0}|{1}...</param>
        public QueryParamConditionBuilder<T> Like(string fieldName, string values)
        {
            var whereItem = new WhereItem_Like(values);
            var current = groupStackPoint.Peek();
            current.Items.Add(new QueryParamConditionItem(fieldName, whereItem));
            return this;
        }

        /// <summary>
        /// 添加like条件，在初始化Param但未开始查询的时候可调用
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="value">值，多个使用|隔开，对应permission.Fields {0}|{1}...</param>
        public QueryParamConditionBuilder<T> StartWith(string fieldName, string values)
        {
            var whereItem = new WhereItem_StartWith(values);
            var current = groupStackPoint.Peek();
            current.Items.Add(new QueryParamConditionItem(fieldName, whereItem));
            return this;
        }

        /// <summary>
        /// 直接传条件组
        /// </summary>
        /// <param name="group"></param>
        public QueryParamConditionBuilder<T> Condition(QueryParamConditionGroup group)
        {
            conditionGroup = group;
            groupStackPoint.Clear();
            groupStackPoint.Push(conditionGroup);
            return this;
        }

        public DataTable Query()
        {            
            if (groupStackPoint.Count > 1) throw new Exception("条件组没有封闭，请检查Or/EndOr或者And/EndAnd的调用");
            return this.context.DoQuery(this.queryParam);
        }


        public T QuerySingle()
        {
            DataTable dt = Query();
            if (dt.Rows.Count > 0)
            {
                T entity = System.Activator.CreateInstance(this.queryParam.entityType, false) as T;
                entity.ParseFromTable(dt);
                return entity;
            }
            else return null;
        }

        public List<T> QueryMany()
        {
            List<T> lst = new List<T>();
            DataTable dt = Query();
            foreach (DataRow dr in dt.Rows)
            {
                T t = System.Activator.CreateInstance(this.queryParam.entityType, false) as T;
                t.ParseFromTableRow(dr);
                lst.Add(t);
            }
            return lst;
        }

    }


    /// <summary>
    /// 条件组
    /// </summary>
    public class QueryParamConditionGroup
    {
        /// <summary>
        /// 添加组之间的添加关系（and \ or）
        /// </summary> 
        public ConditionGroupConjunction Conjunction { get; set; }

        /// <summary>
        /// 条件项
        /// </summary>
        /// <value></value>

        public List<QueryParamConditionItem> Items = new List<QueryParamConditionItem>();

        /// <summary>
        /// 内嵌条件组
        /// </summary>
        /// <value></value>

        public List<QueryParamConditionGroup> Groups = new List<QueryParamConditionGroup>();

        public enum ConditionGroupConjunction { and, or }

        public QueryParamConditionItem FindItem(string fieldName)
        {
            return Items.Find(x => x.FieldName == fieldName);
        }
    }

    /// <summary>
    /// 条件项
    /// </summary>
    public class QueryParamConditionItem
    {
        public QueryParamConditionItem(string fieldName, IWhereItem whereItem)
        {
            FieldName = fieldName;
            WhereItem = whereItem;
        }

        public string FieldName { get; set; }


        public IWhereItem WhereItem { get; set; }
    }



}

